#include "TorrentParser.h"
#include "TorrentInfo.h"
#include "ElementoBencode.h"
#include "IntegerBencode.h"
#include "CadenaBencode.h"
#include "ListaBencode.h"
#include "DiccionarioBencode.h"
#include "BencodeParser.h"
#include "Sha1.h"


TorrentParser::TorrentParser() {


}

TorrentInfo* TorrentParser::parsear(const string &path) {

	ifstream archivo;
	BencodeParser parser;
	DiccionarioBencode* elemento;
	Sha1 sha1;
	archivo.open(path.c_str(), ios::binary);

	if (archivo.is_open()){

		string strAnnounce;
		unsigned long long int piecesLength;
		vector<Archivo> archivos;

		stringstream stream(this->deArchivoAString(&archivo));

		archivo.close();

		elemento = (DiccionarioBencode*)parser.getElemento(stream);

		string hashArchivoStream = this->getHashArchivo(stream,parser);

		string hashArchivo =  sha1.calcularSha1(hashArchivoStream, false);

		CadenaBencode* announce = (CadenaBencode*)(*(elemento->getDiccionario()))["announce"];

		if (announce != NULL){

			strAnnounce = announce->getValor();
		}

		DiccionarioBencode* info = (DiccionarioBencode*)(*(elemento->getDiccionario()))["info"];


		if ( info != NULL){
			if ((*(info->getDiccionario()))["piece length"] != NULL){
				piecesLength = ((IntegerBencode*)((*(info->getDiccionario()))["piece length"]))->getValor();
			}

			/* Si contiene el elemento con clave "files" es un Torrent
			 * MultiArchivo */

			if ((*(info->getDiccionario()))["files"] != NULL){

				ListaBencode* listaArchivos = (ListaBencode*)(*(info->getDiccionario()))["files"];
				list<ElementoBencode*>* lista = listaArchivos->getLista();
				CadenaBencode* name;
				string directorio;

				list<ElementoBencode*>::iterator it;

				if ((name = (CadenaBencode*)(*(info->getDiccionario()))["name"]) != NULL){
					directorio = name->getValor();
				}
				directorio += '/';

				for (it = lista->begin() ; it != lista->end() ; it++){

					DiccionarioBencode* dic = (DiccionarioBencode*)(*it);
					unsigned long long int fileLength;
					ListaBencode* listaPath;
					IntegerBencode* length;
					string path;

					if ((length = (IntegerBencode*)(*(dic->getDiccionario()))["length"]) != NULL) {
						fileLength = length->getValor();
					}

					path = directorio;
					if ((listaPath = (ListaBencode*)(*(dic->getDiccionario()))["path"])  != NULL){

						list<ElementoBencode*>* listaAux = listaPath->getLista();

						list<ElementoBencode*>::iterator it;

						it = listaAux->begin();

						while (it != listaAux->end()){
							path += ((CadenaBencode*)(*it))->getValor();
							it++;
							if ( it != listaAux->end()){
								path += '/';
							}
						}
					}

					Archivo archivo;
					archivo.length = fileLength;
					archivo.path = path;
					archivos.push_back(archivo);
				}
			 } else {
				string path;
				unsigned long long int length;

				if ((*(info->getDiccionario()))["name"] != NULL){
					path = ((CadenaBencode*)((*(info->getDiccionario()))["name"]))->getValor();
				}

				if ((*(info->getDiccionario()))["length"] != NULL){
					length = ((IntegerBencode*)(*(info->getDiccionario()))["length"])->getValor();
				}

				Archivo archivo;

				archivo.length = length;
				archivo.path = path;

				archivos.push_back(archivo);
			}
		}

		string hashes = ((CadenaBencode*)(*(info->getDiccionario()))["pieces"])->getValor();
		vector<string> vectorHash = this->getHashes(hashes);

		parser.borrarRecursosInstanciados((ElementoBencode*)elemento);

		return new TorrentInfo(hashArchivo,strAnnounce,piecesLength,archivos,vectorHash);
	}

	cerr << "Error: No se pudo abrir el archivo, verifique el directorio del mismo. " << endl;
	return NULL;
}


string TorrentParser::deArchivoAString(ifstream *archivo){

	string stream;
	int size;
	char* buffer;

	/* Consigo el size del archivo*/
	archivo->seekg(0 , std::ios::end);
	size = archivo->tellg();
	archivo->seekg(0 , std::ios::beg);

	buffer = new char[size];

	archivo->read(buffer, size);

	stream.append(buffer,size);

    delete[] buffer;

   return stream;
}


vector<string> TorrentParser::getHashes(const string &hashes){

	vector<string> vector;
	string aux;
	int cantidad = (hashes.size()/TAM_HASH);

	for (int i = 0 ; i < cantidad ; i++){
		aux = hashes.substr( i*TAM_HASH , TAM_HASH );
		vector.push_back(aux);
	}

	return vector;
}

string TorrentParser::getHashArchivo(stringstream &stream, const BencodeParser &parser) {

	string aux;

	stream.seekg(parser.getOffsetInicialTorrentInfo());
	unsigned long long int size = parser.getOffsetFinalTorrentInfo() - parser.getOffsetInicialTorrentInfo();
	char *buffer;

	buffer = new char[size];
	stream.read(buffer,size);

	aux.append(buffer,size);

	delete[] buffer;

	return aux;
}

TorrentParser::~TorrentParser() {


}
